Borland Online And The Cobb Group Present:


February, 1994 - Vol. 1 No. 2

Extending TurboVision - Creating a numeric input line

If you use the TurboVision class library to create DOS applications, you've probably created dialog boxes for user input. In some applications, you may need to prompt the user for a number. Unfortunately, none of the native dialog box objects in the library are designed to accept numeric input from the user.

In this article, we'll show you how to create a new class that implements a numeric input line for your dialog boxes. We'll save time by deriving the new class from an existing class in the TurboVision library. First, we'll look at the design of our new class and at the resulting code. Then, we'll demonstrate how to use this class in an application.

Designing The class

One of the biggest selling points of Object Oriented Programming (OOP) is that you can reuse and extend an existing library of code. However, before you can reuse any part of a library, it's important that you know what you want to do and what the library offers. Then, you can look for the best match between the desired behavior and existing library components.

Our numeric input line needs to do three basic things: It must accept numeric input from the keyboard, it must display the numeric data in a dialog box, and it must verify that the value is acceptable. We may want to add more functionality later, but this is the minimum we need.

Within the existing classes of the TurboVision library, TInputLine comes closest to providing the behavior we want. The TInputLine class handles text input from the keyboard and allows you to specify the size of the input line. In addition, you can limit the number of characters a TInputLine object displays.

The TInputLine class is derived from TView, so much of the event-passing code is already written for you. Unfortunately, you'll need to define other member functions.

Since the TDialog class's TView-derived items can contain a value, those items should implement the setData() and getData() member functions for setting and retrieving their data values. However, because the TInputLine class treats the data within the input line as an array of characters, you'd have to convert integral values to strings to set the input line's displayed value.

In the same way, you'd have to convert the ASCII representation of the input value back to an integer to get it when the user finished with the dialog box. We'll have to provide new versions of setData() and getData() to implement this behavior.

By default, members of the TInputLine class always have valid data, so the valid() member function always returns True. If we're going to check the value against upper and lower limits, we'll need to override the valid() member function to prevent the dialog box from accepting invalid data.

We would need to change other functions in the TInputLine class to support streaming, but setData(), getData(), and valid() are the most important functions. Now, let's examine how to implement a numeric input line class.

Looking at the Code

An object of our new class, TNumericInputLine, is a TInputLine object as well. In other words, a numeric input line is a specialized form of input line. Therefore, we'll publicly inherit TNumericInputLine from TInputLine, as shown in the declaration in Listing A.


LISTING A: TNUMINLN.H

#define Uses_TInputLine
#define Uses_MsgBox
#include <tv.h>
#include <stdio.h>

class TNumericInputLine : public TInputLine
{
  public:
    TNumericInputLine(const TRect& bounds,
                      int aMaxLen,
                      int maxValue,
                      int minValue);

    virtual void getData( void *rec );
    virtual void setData( void *rec );
    virtual Boolean valid(ushort command);
protected: int maximumValue; int minimumValue; };

To make sure we can use the correct portions of the TurboVision library, we'll add #define directives for the TInputLine class (so we can derive from it) and for the messageBox() function (which we use in the implementation file). We'll need to add #include directives for the TurboVision header file (TV.H) and for the standard I/O header file (STDIO.H).

We'll declare a constructor that will take four parameters: a TRect rectangle, the number of displayed digits, the maximum integer value, and the minimum integer value. We don't need to explicitly define a destructor since we don't dynamically allocate memory in the constructor.

We'll declare the setData() and getData() virtual member functions that will preset and retrieve the input line's value. A TDialog object calls the setData() and getData() functions for each item in its dialog box.

Finally, we'll declare the valid() virtual member function, which will verify that the value of the input line is between the minimum and maximum values. A TDialog object calls valid() for each item in the dialog box before closing on an OK command.

The implementation of our custom TNumericInputLine class appears in Listing B. Now, let's see how you can use the numeric input line class in a real application.


LISTING B: TNUMINLN.CPP

#include "TNUMINLN.H"
#include <stdio.h>

TNumericInputLine::TNumericInputLine(const TRect& bounds,
                                     int aMaxLen,
                                     int maxValue,
                                     int minValue) :
    TInputLine(bounds, aMaxLen)
{
    maximumValue = maxValue;
    minimumValue = minValue;
}

void TNumericInputLine::getData( void *rec )
{
    sscanf(data, "%d", (int*)rec);
}

void TNumericInputLine::setData( void *rec )
{
    int tempValue = *((int*)rec);
    sprintf(data, "%d", tempValue);
    selectAll(True);
}

Boolean TNumericInputLine::valid( ushort )
{
    int currentValue;
    getData(&currentValue);

    if(currentValue >= minimumValue)
    {   if(currentValue <= maximumValue) 
        { return True; } }

    char temp[80];
    sprintf(temp,
        "Value must be between %d and %d",
        minimumValue,
        maximumValue);
    messageBox(temp, mfOKButton);
    selectAll(True);
    return False;
}

Adding a Numeric Input Line to a Project

To demonstrate the TNumericInputLine class, we'll modify the TurboVision version of Hello World. To start, launch the Borland C++ 3.1 DOS Integrated Development Environment (IDE).

When the IDE Desktop appears, choose Open Project... from the Project menu. In the Open Project File dialog box, enter

\BORLANDC\TVISION\DEMOS\HELLO.PRJ

at the Open Project File input line. Click on the OK button to open the Hello World project file.

Create the declaration file for the TNumericInputLine class by choosing New from the File menu. When the new file window appears, enter the code from Listing A.

When you finish entering the declaration code, choose Save As... from the File menu. In the Save File As dialog box, enter

\BORLAND\TVISION\DEMOS\TNUMINLN.H

at the Save File As input line. Click the OK button to save the file.

Now, you can create the implementation file for the numeric input line class by again choosing New from the File menu. When the new file window appears, enter the code from Listing B.

Next, choose Save As... from the File menu. In the Save File As dialog box, enter

\BORLAND\TVISION\DEMOS\TNUMINLN.CPP

at the Save File As input line. As before, click the OK button to save the file.

Add TNUMINLN.CPP to the Hello World project by choosing Add Item... from the Project menu. When the Add To Project List dialog box appears, enter

TNUMINLN.CPP

at the Name input line. Click the Add button to add this file to the project. When the Add To Project List dialog box reappears, click the Done button to dismiss it.

Open the main source file by double-clicking on the filename HELLO.CPP in the Project window. When the HELLO.CPP window appears, scroll down the file until you locate the member function

void THelloApp::greetingBox() 

Replace the entire greetingBox() function body with the code shown in Listing C below. Be sure to add the #include directive for the TNUMINLN.H declaration file before the function body. Now you're ready to run your modified version of Hello World.


LISTING C: greetingBox()

#include "TNUMINLN.H"    // Include the declaration

void THelloApp::greetingBox()
{   TDialog *d = new TDialog(TRect( 25, 5, 55, 16),
                 "TNumericInputLine"); // New Title Text

    d->insert(new TButton(TRect( 16, 4, 28, 6),
              "Ok",
              cmOK,                    // New cmd value
              bfDefault));             // New button flag

    d->insert(new TButton(TRect( 16, 8, 28, 10),
              "Cancel", cmCancel, bfNormal));

    d->insert(new TNumericInputLine(TRect(3, 5, 8, 6),
              2,                       // # of digits
              10,                      // maximum value
              5));                     // minimum value
              
    int initialValue = 7;              // initial value

    d->setData((void*)(&initialValue));// set value

    deskTop->execView( d );

    destroy(d);
}

Using the Numeric Input Line

Choose Run from the Run menu to begin compiling and running the new version of Hello World. As Figure A shows, the compile status window displays the current condition of the compile process.


Figure A - When you run Hello World from the IDE, the project automatically compiles.

When the compiler finishes compiling the revised application, the Desktop for the Hello World application appears. Choose Greeting... from the Hello menu. A dialog box with a numeric input line will appear, as shown in Figure B.


Figure B - The initial value of "7" is in the numeric input line when the dialog box first appears.

Since the greetingBox() function establishes that the minimum value should be 5 and the maximum value 10, enter 2 at the numeric input line and then click the OK button. A warning dialog box appears to tell you the correct range of values, as shown in Figure C.


Figure C - If you enter an unacceptable value, you'll see a warning dialog box.

Click the OK button in the warning dialog box. When the TNumericInputLine dialog box reappears, enter the value 8 at the numeric input line and click the OK button. Since this is an acceptable value, the dialog box closes. Choose Exit from the Hello menu to quit the Hello World application.

Conclusion

OOP languages like Borland C++ allow you to take advantage of other programmers' efforts and at the same time customize the behavior of other programmers' classes. By carefully examining the TurboVision class library, we were able to use less than one page of code to create a new class, TNumericInputLine, from an existing class.

Return to the Borland C++ Developer's Journal index

Subscribe to the Borland C++ Developer's Journal


Copyright (c) 1996 The Cobb Group, a division of Ziff-Davis Publishing Company. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis Publishing Company is prohibited. The Cobb Group and The Cobb Group logo are trademarks of Ziff-Davis Publishing Company.